
(function(window) {
  /**
   * Get the root of this script. This is relative to the HTML page.
   */
  var root = (function(){
    var scr = document.getElementsByTagName('script');
    // This will always be the current script.
    scr = scr[scr.length - 1];
    return scr.src.replace(/\/[^\/]*$/, "").replace(/^http:\/\/[^\/]+/, "");
  })();

  // This is so we don't include multiple time the same file.
  var include_list_ = {};

  window.execute_script = function(filename) {
    var r = new XMLHttpRequest();
    r.open("GET", filename, false);
    r.send();

    try {
      var f = new Function(r.responseText);
      f.name = filename;
      return f();
    } catch(e) {
      throw("Error: " + e + "\n  during loading of \"" + filename + "\"");
    }
  }

  /**
   * Define the namespace and include the Javascript file synchronously.
   * @export
   **/
  window.include = function(namespace) {
    if (namespace === undefined)
      throw "Error: parameter namespace mandatory."

    if (include_list_[namespace]) {
      return;
    }

    var c = root + '/' + namespace.replace(/\./g, '/') + '.js';
    try {
      var x = execute_script(c);
      if (typeof x == "function") {
        x(window);
      }
    } catch(e) {
      throw e;
    }
    include_list_[namespace] = true;
  };

  window.define = function(namespace, opt_object) {
    var curr = window;
    var ns = namespace.split(".");
    var i = 0;
    var ii = ns.length;
    for(; i < ii - 1; ++i) {
      var v = ns[i];
      curr[v] = curr[v] || {};
      curr = curr[v];
    }
    var x = ns[i];
    if (typeof opt_object != "undefined") {
      curr[x] = opt_object;
    }
    else {
      curr[x] = curr[x] || {};
    }
    return curr[x];
  }

  function extend(obj, extension, override) {
    var prop;
    if (override === false) {
      for (prop in extension)
        if (!(prop in obj))
          obj[prop] = extension[prop];
    } else {
      for (prop in extension)
        obj[prop] = extension[prop];
      if (extension.toString !== Object.prototype.toString)
        obj.toString = extension.toString;
    }
  }

  var func_name_regex_ = /^[a-zA-Z0-9_\.]*?([a-zA-Z0-9_]+)$/;
  func_name_regex_.compile(func_name_regex_);
  var constructing_ = false;

  window.new_class = 
    function(name, ctor, opt_proto, opt_parent, opt_mixin) {
      opt_proto = opt_proto || {};
      opt_parent = opt_parent || function() {};
      opt_mixin = opt_mixin || [];
      var c;
      if (name) {
        var fname = func_name_regex_.exec(name);
        // We eval this so that the name is kept for the type of the class.
        if (!fname) throw "Invalid name.";
        eval("function " + fname[1] + "() {" +
            "if (constructing_) return;" +
            "this.constructor = ctor;" +
            "return ctor.apply(this, slice(arguments));" +
          "}");
        c = define(name, eval(fname[1]));
      }
      else {
        c = function $$Anonymous() {
          if (constructing_) return;
          this.constructor = ctor;
          return ctor.apply(this, slice(arguments));
        }
      }
      var old = constructing_;
      constructing_ = true;
      c.prototype = new opt_parent;
      constructing_ = old;

      c.prototype.super = opt_parent.prototype;
      c.prototype.parent = function() {
          return opt_parent.apply(this, slice(arguments));
        };
      for(var x in opt_proto) {
        c.prototype[x] = opt_proto[x];
      }
      // Mixins applied at last.
      opt_mixin.each(function(i,o) {
          var n = o();
          for(var x in n) {
            c.prototype[x] = n[x];
          }
        });
      return c;
    }

  window.slice = function(obj, i) {
    return Array.prototype.slice.call(obj, i);
  };

  window.bind = function(f, opt_this, var_args) {
    var args = slice(arguments);
    args = args.slice().slice();

    return function() {
      return f.apply(opt_this, args.concat(slice(arguments)));
    };
  }

  Array.prototype.each = function(func) {
    for (var i = 0, ii = this.length; i < ii; ++i)
      func(i, this[i]);
    return this;
  }
})(window);

